#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__
/*日志器模块
    1.先设计一个日志器的基类
    2.根据基类派生出不同的日志器
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "util.hpp"
#include "level.hpp"
#include "sink.hpp"
#include "format.hpp"
#include "looper.hpp"
#include <memory>
#include <mutex>
#include <atomic>
#include <cstdarg>
#include <cassert>
#include <unordered_map>

namespace logslearn
{
    //  设计日志器基类
    class Logger
    {
        // 公有
    public:
        // 基类指针，用来控制继承子类的对象
        using ptr = std::shared_ptr<Logger>;
        // 构造函数
        Logger(const std::string &logger_name, loglevel::value level, Formatter::ptr &formatter, std::vector<LogSink::ptr> &sinks) : _logger_name(logger_name), _limit_level(level), _formatter(formatter), _sliks(sinks.begin(), sinks.end()) {}
        // 操作方法
        // 获取日志器名称
        const std::string &name()
        {
            return _logger_name;
        }
        // 构造日志消息对象并进行格式化，得到格式化后的日志消息字符串--然后进行落地输出，5个等级
        void debug(const std::string &file, size_t line, const std::string &fmt, ...)
        { // 日志的输出操作
            // 1.判断当前的日志是否达到输出等级
            if (loglevel::value::DEBUG < _limit_level)
            {
                return;
            } // 没有达到输出等级
            // 2.对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!!\n";
                return;
            }
            va_end(ap); // 将ap指针置空
            // 代码一样，可以封装成一个函数
            serialize(loglevel::value::DEBUG, file, line, res);
            free(res); // 将指针释放掉
        }
        void info(const std::string &file, size_t line, const std::string &fmt, ...)
        { // 日志的输出操作
            // 1.判断当前的日志是否达到输出等级
            if (loglevel::value::INFO < _limit_level)
            {
                return;
            } // 没有达到输出等级
            // 2.对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!!\n";
                return;
            }
            va_end(ap); // 将ap指针置空
            // 代码一样，可以封装成一个函数
            serialize(loglevel::value::INFO, file, line, res);
            free(res); // 将指针释放掉
        }
        void warn(const std::string &file, size_t line, const std::string &fmt, ...)
        { // 日志的输出操作
            // 1.判断当前的日志是否达到输出等级
            if (loglevel::value::WARN < _limit_level)
            {
                return;
            } // 没有达到输出等级
            // 2.对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!!\n";
                return;
            }
            va_end(ap); // 将ap指针置空
            // 代码一样，可以封装成一个函数
            serialize(loglevel::value::WARN, file, line, res);
            free(res); // 将指针释放掉
        }
        void error(const std::string &file, size_t line, const std::string &fmt, ...)
        { // 日志的输出操作
            // 1.判断当前的日志是否达到输出等级
            if (loglevel::value::ERROR < _limit_level)
            {
                return;
            } // 没有达到输出等级
            // 2.对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!!\n";
                return;
            }
            va_end(ap); // 将ap指针置空
            // 代码一样，可以封装成一个函数
            serialize(loglevel::value::ERROR, file, line, res);
            free(res); // 将指针释放掉
        }
        void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
        { // 日志的输出操作
            // 1.判断当前的日志是否达到输出等级
            if (loglevel::value::FATAL < _limit_level)
            {
                return;
            } // 没有达到输出等级
            // 2.对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!!\n";
                return;
            }
            va_end(ap); // 将ap指针置空
            // 代码一样，可以封装成一个函数
            serialize(loglevel::value::FATAL, file, line, res);
            free(res); // 将指针释放掉
        }
        void serialize(loglevel::value level, const std::string &file, size_t line, char *str)
        {
            // 3.构造LogMsg对象
            LogMsg msg(level, line, file, _logger_name, str); // 传入等级、行号、文件、日志器、有效信息
            // 4.通过格式化工具对LogMsg进行格式化，得到格式化后的日志字符串
            std::stringstream ss;
            _formatter->format(ss, msg);
            // 5.进行日志落地
            log(ss.str().c_str(), ss.str().size()); // 日志字符串和长度
        }

        // 日志落地,抽象接口完成实际的落地输出——不同的日志器会有不同的实际落地方式
        virtual void log(const char *data, size_t len) = 0;

    protected:
        std::mutex _mutex;                         // 互斥锁
        std::string _logger_name;                  // 日志器的名字
        std::atomic<loglevel::value> _limit_level; // 限制日志等级,atomic原子操作的意思是该操作执行过程中不能被中断，该操作要么不执行，要么全部执行，不存在执行一部分的情况。
        Formatter::ptr _formatter;                 // 控制格式化模块的对象
        std::vector<LogSink::ptr> _sliks;          // 这是一个数组，数组里存放日志落地方式的对象
    };
    // 派生出同步日志器
    class SyncLogger : public Logger
    {
        // 构造函数
    public:
        SyncLogger(const std::string &logger_name,
                   loglevel::value level,
                   Formatter::ptr &formatter,
                   std::vector<LogSink::ptr> &sinks) : Logger(logger_name, level, formatter, sinks) {}

    protected:
        // 重写虚函数,同步日志器是将日志通过落地模块句柄进行日志落地
        void log(const char *data, size_t len)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            // 是空
            if (_sliks.empty())
            {
                return;
            }
            // 不是空
            for (auto &sink : _sliks)
            {
                sink->log(data, len);
            }
        }
    };
    // 派生出异步日志器
    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(const std::string &logger_name,
                    loglevel::value level,
                    Formatter::ptr &formatter,
                    std::vector<LogSink::ptr> &sinks,
                    AsyncType looper_type) : Logger(logger_name, level, formatter, sinks),
                                             _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realLog, this, std::placeholders::_1), looper_type)) {}
        // 将数据写入缓冲区
        void log(const char *data, size_t len)
        {
            // 消息入队操作，不需要加锁，已经是线程安全得了
            _looper->push(data, len);
        }
        // 设计一个实际落地函数（将缓冲区里的数据进行落地）
        void realLog(Buffer &buf)
        {
            if (_sliks.empty())
                return;
            for (auto &sink : _sliks)
            {
                sink->log(buf.begin(), buf.readAbleSize());
            }
        }

    private:
        AsyncLooper::ptr _looper;
    };

    enum class LoggerType
    {
        LOGGER_SYNC, // 同步日志器
        LOGGER_ASYNC // 异步日志器

    };
    // 使用建造者模式来构造日志器，而不要用户直接去构造日志器，简化了用户的使用复杂度
    // 1.抽象一个日志器建造者类
    //   1.设置日志器类型
    //   2.不同类型的日志器的创建放到同一个日志器建造者类中完成。
    class LoggerBuilder
    {
    public:
        // 构造函数
        LoggerBuilder() : logger_type(LoggerType::LOGGER_SYNC),
                          _limit_level(logslearn::loglevel::value::DEBUG),
                          _looper_type(AsyncType::ASYNC_SAFE) {}
        void buildEnabeUnSafeAsync()
        {
            _looper_type = AsyncType::ASUNC_UNSAFE; // 非安全模式
        }
        void buildLoggerType(LoggerType type)
        {
            logger_type = type;
        }
        void buildLoggerName(const std::string &name)
        {
            _logger_name = name;
        }
        void buildLoggerLevel(loglevel::value level)
        {
            _limit_level = level;
        }
        // 构造一个格式化器
        void buildLoggerFormatter(const std::string &pattern)
        {
            _formatter = std::make_shared<Formatter>(pattern);
        }
        // 一个日志器可以有多个不同的落地方式
        template <typename SinkType, typename... Args>
        void buildSink(Args &&...arg)
        {
            LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(arg)...); // 完美转发
            _sliks.push_back(psink);
        }

        // 完成我们的日志器构建
        virtual Logger::ptr build() = 0;

    protected:
        // 需要管理的数据，也就是建造的零部件
        AsyncType _looper_type;       // 异步线程的安全模式
        LoggerType logger_type;       // 日志器类型
        std::string _logger_name;     // 日志器的名字
        loglevel::value _limit_level; // 限制日志等级,atomic原子操作的意思是该操作执行过程中不能被中断，该操作要么不执行，要么全部执行，不存在执行一部分的情况。
        Formatter::ptr _formatter;    // 控制格式化模块的对象
        std::vector<LogSink::ptr> _sliks;
    };
    // 2.派生出具体的建造者类——局部日志器的建造者 & 全局日志器建造者（后边添加了全局单例管理之后，将日志器添加全局管理）
    // 局部日志器的建造者
    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            // 必须要有日志器名称
            assert(_logger_name.empty() == false);
            // 必须要有formatter//必须要有格式化器，没有就要创建
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            // 如果没有落地方式就给它添加一个标准输出的默认落地方式
            if (_sliks.empty())
            {
                buildSink<StdoutSink>();
            }
            // 如果类型为LOGGER_ASYNC，那么日志器为异步日志器
            if (logger_type == LoggerType::LOGGER_ASYNC)
            {
                // 返回异步日志器对象
                return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sliks, _looper_type);
            }
            // 返回同步日志器的对象
            return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sliks); // r日志器名字，等级，格式化，落地方式
        }
    };
  
    // 日志器管理模块
    class LoggerManager
    {
    public:
            // 5. 获取单例句柄
        static LoggerManager& getInstance()
        {
            // 在c++11之后，针对静态局部变量，编译器在编译的层面上实现了线程安全
            // 当静态局部变量在没有构造完成之前，其他的线程进入就会阻塞
            static LoggerManager eton;
            return eton;
        }
        // 1添加日志器管理
        void addLogger(Logger::ptr &logger)
        {
            // 如果已经有了日志器，就不需要再添加
            if (hasLogger(logger->name()))
                return;
            std::unique_lock<std::mutex> lock(_mutex); // 添加日志器之前加锁
            _loggers.insert(std::make_pair(logger->name(), logger));
        }
        // 2.判断是否管理了指定名称的日志器
        bool hasLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex); // 判断之前加锁
            auto it = _loggers.find(name);             // 查找日志器
            if (it == _loggers.end())
            {
                // 代表没找到
                return false;
            }
            return true;
        }
        // 3.获取指定名称的日志器
        Logger::ptr getLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex); // 获取之前加锁
            auto it = _loggers.find(name);             // 查找日志器
            if (it == _loggers.end())
            {
                // 代表没找到，返回一个空的智能指针
                return Logger::ptr();
            }
            return it->second; // 日志器所对应的值
        }
        // 4.获取默认日志器
        Logger::ptr rootLogger()
        {
            return _root_logger;
        }

    private:
        // 构造函数私有化
        LoggerManager()
        {
            // 构造一个日志器建造者LocalLoggerBuilder()只能使用本地建造者不能使用全局建造者
            std::unique_ptr<logslearn::LoggerBuilder> builder(new logslearn::LocalLoggerBuilder());
            builder->buildLoggerName("root");
            _root_logger = builder->build(); // 建造者构建对象，没有建造的就用默认对象
            // 把默认构造的日志器管理起来
            _loggers.insert(std::make_pair("root", _root_logger));
        }
        LoggerManager(const LoggerManager&) = delete;
    private:
        // 1.默认日志器
        Logger::ptr _root_logger;
        // 2.所管理的日志器
        std::unordered_map<std::string, Logger::ptr> _loggers;
        // 3.互斥锁
        std::mutex _mutex;
    };
  //设计一个全局的日志器建造者-在局部的日志器建造者上增加一个功能：将日志器添加到单例对象中
    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            // 必须要有日志器名称
            assert(_logger_name.empty() == false);
            // 必须要有formatter//必须要有格式化器，没有就要创建
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            // 如果没有落地方式就给它添加一个标准输出的默认落地方式
            if (_sliks.empty())
            {
                buildSink<StdoutSink>();
            }
            //默认日志器
            Logger::ptr logger;
             // 如果类型为LOGGER_ASYNC，那么日志器为异步日志器
            if (logger_type == LoggerType::LOGGER_ASYNC)
            {
                // 返回异步日志器对象
                logger=std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sliks, _looper_type);
            }else{
                // 返回同步日志器的对象
                logger=std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sliks); // r日志器名字，等级，格式化，落地方式
            }
            
            //把日志器添加到日志器管理器中
            LoggerManager::getInstance().addLogger(logger);
            // 返回同步日志器的对象
            return logger;
        }
    };
}
#endif